Jackson ডিফল্টভাবে abstract classes বা interfaces সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করতে পারে না, কারণ এগুলোর ইনস্ট্যান্স তৈরি করা সম্ভব নয়। তবে, সঠিক কনফিগারেশন এবং কাস্টম পলিসি ব্যবহারের মাধ্যমে Jackson এ abstract classes এবং interfaces হ্যান্ডল করা সম্ভব।
Abstract Class এবং Interface কেন সমস্যা তৈরি করে?
- Abstract Class: এটি সরাসরি ইনস্ট্যান্সিয়েট করা যায় না।
- Interface: এটি শুধুমাত্র মেথডের সিগনেচার সংজ্ঞায়িত করে, ইনস্ট্যান্সিয়েট করা যায় না।
এগুলোর জন্য Jackson কে জানাতে হবে:
- ডেটার আসল টাইপ (Sub-Class)।
- কিভাবে সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করতে হবে।
সমাধানের পদ্ধতি
১. Type Information অন্তর্ভুক্ত করা
Jackson-এর @JsonTypeInfo এবং @JsonSubTypes অ্যানোটেশন ব্যবহার করে টাইপ ইনফরমেশন সংজ্ঞায়িত করা যায়।
উদাহরণ:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
// Abstract Class
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // টাইপ ইনফরমেশন সংরক্ষণ
include = JsonTypeInfo.As.PROPERTY,
property = "type" // JSON-এ "type" ফিল্ড যোগ হবে
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Car.class, name = "car"),
@JsonSubTypes.Type(value = Truck.class, name = "truck")
})
abstract class Vehicle {
public String brand;
}
// Sub-Class 1
class Car extends Vehicle {
public int seatingCapacity;
public Car() {}
}
// Sub-Class 2
class Truck extends Vehicle {
public double payloadCapacity;
public Truck() {}
}
Serialization:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class SerializationExample {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Vehicle car = new Car();
car.brand = "Toyota";
((Car) car).seatingCapacity = 5;
String json = mapper.writeValueAsString(car);
System.out.println("Serialized JSON:\n" + json);
}
}
Output:
{
"type": "car",
"brand": "Toyota",
"seatingCapacity": 5
}
Deserialization:
public class DeserializationExample {
public static void main(String[] args) throws Exception {
String json = "{ \"type\": \"truck\", \"brand\": \"Ford\", \"payloadCapacity\": 10000.0 }";
ObjectMapper mapper = new ObjectMapper();
Vehicle vehicle = mapper.readValue(json, Vehicle.class);
System.out.println("Deserialized Vehicle: " + vehicle.brand + ", Type: " + vehicle.getClass().getSimpleName());
}
}
Output:
Deserialized Vehicle: Ford, Type: Truck
২. Mix-in Annotations ব্যবহার করা
যদি আপনার কাছে Abstract Class বা Interface-এর সোর্স কোড না থাকে, তবে Mix-in Annotations ব্যবহার করে টাইপ ইনফরমেশন যোগ করা সম্ভব।
উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
// Abstract Class
abstract class Shape {
public String color;
}
// Sub-Class
class Circle extends Shape {
public double radius;
}
// Mix-in Annotation
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Circle.class, name = "circle")
})
abstract class ShapeMixIn {}
কনফিগার করা:
public class MixInExample {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Shape.class, ShapeMixIn.class); // Mix-in যোগ করা
Shape shape = new Circle();
shape.color = "Red";
((Circle) shape).radius = 5.5;
String json = mapper.writeValueAsString(shape);
System.out.println("Serialized JSON:\n" + json);
Shape deserializedShape = mapper.readValue(json, Shape.class);
System.out.println("Deserialized Shape Type: " + deserializedShape.getClass().getSimpleName());
}
}
Output:
{
"type": "circle",
"color": "Red",
"radius": 5.5
}
৩. Custom Serializer এবং Deserializer ব্যবহার করা
Custom Serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
public class VehicleSerializer extends StdSerializer<Vehicle> {
public VehicleSerializer() {
super(Vehicle.class);
}
@Override
public void serialize(Vehicle value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("type", value.getClass().getSimpleName().toLowerCase());
gen.writeStringField("brand", value.brand);
if (value instanceof Car) {
gen.writeNumberField("seatingCapacity", ((Car) value).seatingCapacity);
} else if (value instanceof Truck) {
gen.writeNumberField("payloadCapacity", ((Truck) value).payloadCapacity);
}
gen.writeEndObject();
}
}
Custom Deserializer:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class VehicleDeserializer extends StdDeserializer<Vehicle> {
public VehicleDeserializer() {
super(Vehicle.class);
}
@Override
public Vehicle deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String type = node.get("type").asText();
Vehicle vehicle;
if ("car".equals(type)) {
vehicle = new Car();
((Car) vehicle).seatingCapacity = node.get("seatingCapacity").asInt();
} else if ("truck".equals(type)) {
vehicle = new Truck();
((Truck) vehicle).payloadCapacity = node.get("payloadCapacity").asDouble();
} else {
throw new IllegalArgumentException("Unknown type: " + type);
}
vehicle.brand = node.get("brand").asText();
return vehicle;
}
}
Module Configuration:
public class CustomSerializerExample {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(Vehicle.class, new VehicleSerializer());
module.addDeserializer(Vehicle.class, new VehicleDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Vehicle car = new Car();
car.brand = "Honda";
((Car) car).seatingCapacity = 4;
String json = mapper.writeValueAsString(car);
System.out.println("Custom Serialized JSON:\n" + json);
Vehicle deserializedVehicle = mapper.readValue(json, Vehicle.class);
System.out.println("Custom Deserialized Vehicle: " + deserializedVehicle.brand);
}
}
উপকারিতা
- Abstract Class এবং Interface হ্যান্ডল করা সহজতর।
- Runtime Polymorphism সাপোর্ট।
- Custom Serialization/Deserialization এর মাধ্যমে নির্দিষ্ট নিয়ম প্রয়োগ।
Jackson-এর এই ফিচারগুলো ব্যবহার করে আপনি Abstract Classes এবং Interfaces নিয়ে সহজে কাজ করতে পারবেন।
Read more